package com.xls.pet.service.impl;

import cn.hutool.core.util.IdUtil;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.update.UpdateChain;
import com.xls.pet.common.ResultCode;
import com.xls.pet.entity.Item;
import com.xls.pet.entity.WorkOrder;
import com.xls.pet.exception.BusinessException;
import com.xls.pet.mapper.ItemMapper;
import com.xls.pet.mapper.WorkOrderMapper;
import com.xls.pet.service.MinioService;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.xls.pet.entity.table.ItemTableDef.ITEM;
import static com.xls.pet.entity.table.WorkOrderTableDef.WORK_ORDER;

@Service
@Slf4j
public class MinioServiceImpl implements MinioService {

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private WorkOrderMapper workOrderMapper;

    @Value("${minio.url}")
    private String url;

    @Value("${minio.bucket}")
    private String bucket;

    public String uploadFile(MultipartFile file) {
        try {
            String name = file.getOriginalFilename();
            if (name == null) {
                throw new BusinessException(ResultCode.UPLOAD_FILE_ERROR);
            }
            String fileName = IdUtil.simpleUUID() + name.substring(name.lastIndexOf("."));
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucket)
                            .object(fileName)
                            .stream(inputStream, inputStream.available(), -1)
                            .contentType(file.getContentType())
                            .build()
            );
            return url + fileName;
        } catch (Exception e) {
            throw new BusinessException(ResultCode.UPLOAD_FILE_ERROR);
        }
    }

    public void deleteFile(String filename) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(bucket).object(filename.substring(url.length())).build());
        } catch (Exception e) {
            throw new BusinessException(ResultCode.DELETE_FILE_ERROR);
        }
    }

    public void deleteNoUsageFile() {
        Set<String> minioUrlSet = new HashSet<>();
        Iterable<Result<io.minio.messages.Item>> pet = minioClient.listObjects(ListObjectsArgs.builder().bucket("pet").build());
        pet.forEach(itemResult -> {
            try {
                String objectName = itemResult.get().objectName();
                if ("home.jpg".equals(objectName) || "avatar.jpg".equals(objectName)) {
                    return;
                }
                minioUrlSet.add(url + objectName);
            } catch (Exception e) {
                throw new BusinessException(ResultCode.GET_FILE_ERROR);
            }
        });
        List<String> itemList = QueryChain.of(itemMapper)
                .select(ITEM.IMAGE).from(ITEM)
                .where(ITEM.IMAGE.isNotNull())
                .listAs(String.class);
        Set<String> itemUrlSet = new HashSet<>(itemList);

        List<String> workOrderList = QueryChain.of(workOrderMapper)
                .select(WORK_ORDER.IMAGE).from(WORK_ORDER)
                .where(WORK_ORDER.IMAGE.isNotNull())
                .listAs(String.class);
        Set<String> workOrderUrlSet = new HashSet<>(workOrderList);

        deleteNoUsageUrl(minioUrlSet, itemUrlSet, workOrderUrlSet);
    }

    /**
     * 删除minio中没有被引用的文件
     */
    private void deleteNoUsageUrl(Set<String> minioUrlSet, Set<String> itemUrlSet, Set<String> workOrderUrlSet) {
        Set<String> minioNoUsageUrl = new HashSet<>(minioUrlSet);
        // 从minio中的所有图片url删除物品和工单中的所有图片url
        minioNoUsageUrl.removeAll(itemUrlSet);
        minioNoUsageUrl.removeAll(workOrderUrlSet);
        log.info("delete minio no usage url: {}", minioNoUsageUrl);
        minioNoUsageUrl.forEach(url -> {
            try {
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucket).object(url.substring(url.lastIndexOf("/"))).build());
            } catch (Exception e) {
                throw new BusinessException(ResultCode.DELETE_FILE_ERROR);
            }
        });

        // 删除物品中失效的url
        Set<String> itemNoUsageUrl = new HashSet<>(itemUrlSet);
        itemNoUsageUrl.removeAll(minioUrlSet);
        if (!itemNoUsageUrl.isEmpty()) {
            log.info("delete item no usage url: {}", itemNoUsageUrl);
            UpdateChain.of(Item.class)
                    .set(Item::getImage, null)
                    .where(Item::getImage)
                    .in(itemNoUsageUrl)
                    .update();
        }

        // 删除工单中失效的url
        Set<String> workOrderNoUsageUrl = new HashSet<>(workOrderUrlSet);
        workOrderNoUsageUrl.removeAll(minioUrlSet);
        if (!workOrderNoUsageUrl.isEmpty()) {
            log.info("delete worker order no usage url: {}", workOrderNoUsageUrl);
            UpdateChain.of(WorkOrder.class)
                    .set(WorkOrder::getImage, null)
                    .where(WorkOrder::getImage)
                    .in(workOrderNoUsageUrl)
                    .update();
        }
    }

}
